
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>

#define MAXFILES    20
#define SERV        "80"

struct file {

    char * f_name;
    char * f_host;
    char   f_fd;
    int    f_flags;
    pthread_t   f_tid;

}file[MAXFILES];

#define F_CONNECTING    1
#define F_READING       2
#define F_DONE          4
#define F_JOINED        8

#define MAXLINE         1500

#define GET_CMD "GET %s HTTP/1.1\r\n\r\n"   // CRLF CRLF

int nconn,   nfiles,     nlefttoconn, nlefttoread;

int ndone = 0;
pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  ndone_cond  = PTHREAD_COND_INITIALIZER;

void * do_get_read(void * ptr);

void home_page(const char * host, const char * fname);
void write_get_cmd(struct file * );

int tcp_connect(const char * host, int port);

int min(int a, int b){  return a > b ? b : a;  }

// build : gcc -Wall -Wshadow -g web.c -o web -lpthread
int main(int argc, char * argv[]) {

    int i, maxnconn;
    pthread_t   tid;
    
    struct file * fptr;
    if (argc < 5) {

        printf("usage: web <#conns> <IpAddr> <homepage> file1 ....");
        exit(-1);
    }
    maxnconn = atoi(argv[1]);

    // 文件数目
    nfiles = min(argc - 4, MAXFILES);
    for (i = 0; i < nfiles; i++) {

        file[i].f_name  = argv[i + 4];
        file[i].f_host  = argv[2];
        file[i].f_flags = 0;
    }
    printf("nfiles = %d\n", nfiles);
    home_page(argv[2], argv[3]);

    nlefttoread = nlefttoconn = nfiles;
    nconn = 0;
    while(nlefttoread > 0) {

        while(nconn < maxnconn && nlefttoconn > 0) {

            for (i = 0; i < nfiles; i++) {

                if (file[i].f_flags == 0) {

                    break;
                }
            }
            if (i == nfiles) {

                printf("nlefttoconn = %d\t but nothing found", nlefttoconn);
                exit(1);                
            }
            file[i].f_flags = F_CONNECTING;
            // 创建线程
            pthread_create(&tid, NULL, &do_get_read, &file[i]);

            file[i].f_tid = tid;
            nconn++;
            nlefttoconn--;
        }
        // wait for thread to terminate
        pthread_mutex_lock(&ndone_mutex);
        while (ndone == 0) {

            pthread_cond_wait(&ndone_cond, &ndone_mutex);
        }
        for (i = 0; i < nfiles; i++) {

            if (file[i].f_flags & F_DONE) {

                // &fptr 是返回值
                pthread_join(file[i].f_tid, (void **)&fptr);
                
                if (&file[i] != fptr) {

                    printf("{\"code\":2,\"message\":\"file[i] != fptr\"}");
                    exit(2);
                }
                fptr->f_flags = F_JOINED;   // clears F_DONE

                ndone--;
                nconn--;
                nlefttoread--;
                printf("thread %ld for %s done\n", fptr->f_tid, fptr->f_name);
            }
        }
        pthread_mutex_unlock(&ndone_mutex);
    }
    return EXIT_SUCCESS;
}


void * do_get_read(void * ptr) {

    int fd, n;
    char line[MAXLINE];
    struct file * fptr;

    fptr = (struct file * )(ptr);

    // connect to server
    // fd = connect

    fptr->f_fd = fd;
    printf("do get read for %s, fd %d, thread %ld\n", fptr->f_name, fd, fptr->f_tid);

    write_get_cmd(fptr);

    for (; ; ) {

        if ((n = read(fptr->f_fd, line, MAXLINE)) == 0) {

            break;
        }
        printf("read %d bytes from %s\n", n, fptr->f_name);
    } 
    printf("end-of-file on %s\n", fptr->f_name);
    close(fptr->f_fd);
    fptr->f_flags = F_DONE;
    pthread_cond_signal(&ndone_cond);

    return(fptr);
}


void write_get_cmd(struct file * fptr) {

    char buffer[MAXLINE];
    bzero(buffer, MAXLINE);

    snprintf(buffer, MAXLINE, GET_CMD, fptr->f_name);

    size_t n = write(fptr->f_fd, buffer, strlen(buffer) + 1);
    printf("send http request %ld bytes for %s.\n", n, fptr->f_name);

    fptr->f_flags = F_READING;
}

void home_page(const char * host, const char * fname) {

    int fd, n;
    char line[MAXLINE];

    // fd = 
    n = snprintf(line, MAXLINE, GET_CMD, fname);
    write(fd, line, n);
    for (; ; ) {

        if ((n = read(fd, line, MAXLINE)) == 0) {

            break;
        }
        printf("read %d bytes from page\n", n);
    }
    printf("end-of-file on home page\n");
    close(fd);
}

